package controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;

/**
 * <p>Title: FireWall Project. UI Module </p>
 * <p>Description: The UI module is a conector between the user level and application level (iptable & logger)
 * 					it provide to the user to transfer orders (stop, start, clean, load, save, help) ,
 * 					create or remove rules in the firewall, show all firewall rules, change log state,
 * 					clean & read log</p>
 * <p>Copyright: Copyright (c) 2006</p>
 * @author FW team
 * @version 1.0
 */
public class UI {
	final String sbinDirConst = "/sbin/"; 
	final String shFileConst  = "/bin/sh -c ";
	final String iptablesFile =  sbinDirConst +  "iptables"; 
	final String logParserFileConst = "./log_parser"; 
	final String IPTblLogFileNameConst = "./ipcontext.log";
	final String IPTblFileNameConst= "./ipcontext.rule";
	 String IPTblFileNameTmp= "./.tmp.ipcontext.rule";
	final String PatternFileIn = "protocols.in";
	final String PatternFileOut= "protocols.out";
	final String LoggerStateFileConst = "./.loggerState.ON";
	final String ipContentTypeConst = "ipcontext";
	final String ipTableTypeConst = "keystring2";
	final String loggerIpContentConst = "--ipcontext_log ";
	final String loggerIpTableConst = " -j LOG --log-prefix \"OSLFW:\" ";
	final String iptableTableDefaultConst = "mangle ";
	final String iptableTargetDefaultConst= "POSTROUTING ";
	final String iptableTargetDefaultOutConst= "OUTPUT ";
	final String iptableTargetDefaultInConst=  "INPUT ";

	// This flag just for debugging. for run this module in run mode set it to false
	// true =debug mode  false = run mode
	final boolean DebugModeConst      = false;
	final boolean DebugModePrintConst = false;
	
	
	/**
	 * This method start the FW by getting the rules file and load it into the iptable.
	 * @param N/A
	 * @throws IOException - if failed in creation a new file or in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void start() throws IOException, RuntimeException,Exception
	{
		clean();
		load (IPTblFileNameConst);
	}

	/**
	 * This method stop the FW by saving all the iptable's rules into the FW's rules file
	 * then delete all the rules from the iptable.
	 * @param N/A
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void stop() throws IOException, RuntimeException
	{
		save(IPTblFileNameConst);
		clean();
	}

	/**
	 * This method remove rule from the IPTable by given index
	 * @param p_index - the index of the rule that should be remove
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void remove(boolean p_inDirect, int p_index) throws IOException, RuntimeException
	{
		// TODO: maybe should check if the rule number exist
		run(iptablesFile
				+ " -t  " + iptableTableDefaultConst
				+ " -D  " + (( p_inDirect ) ? iptableTargetDefaultInConst  : iptableTargetDefaultOutConst)
				+ p_index + " " );
	}

	/**
	 * This method show the iptable's help. It's not console application HELP !!!
	 * @param N/A
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void help() throws IOException, RuntimeException
	{
		final String helpCommandConst =  iptablesFile + " -m ipcontext -h" ;
		System.out.println(" Usethis command in order to get help: "+ helpCommandConst+" \n");
		run(helpCommandConst); 
		
	}

	/**
	 * This method save all the FW rules from the IPTable into the FW's rules file
	 * * @param p_fileName - the file that contain the FW rules.
	 * 					   If no file transfered the default IPtableFile will be load
	 *   @throws IOException - if failed in exe command I/O exception occur
	 *   @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void save(String p_fileName) throws IOException, RuntimeException
	{
		String fileName = p_fileName;

		if (p_fileName.equals(""))
			fileName = IPTblFileNameConst;

		run(  sbinDirConst + "iptables-save > " + fileName + " ");
	}

	/**
	 * This method load the FW's rules file into the IPTable
	 * * @param N/A
	 *   @throws IOException - if file not exist & creation of the file failed or if failed in exe command I/O exception occur
	 *   @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void load(String p_fileName) throws IOException, RuntimeException,Exception
	{
		String fileName = p_fileName;

		if (p_fileName.equals(""))
			fileName = IPTblFileNameConst;
		// if file not exist create new file
		File tmpFile = new File(fileName);
		if(!tmpFile.exists())
			throw new Exception ( "There is no specified file!:" + fileName );
		
			//tmpFile.createNewFile();
		run(  sbinDirConst + "iptables-restore < " + fileName);
	}

	/**
	 * This method add match rule into the IPTable. match rule it rule that base on port & protocol
	 * * @param p_port - the port that the rule refer to
	 * 	 @param p_protocol - the protocol that the rule refer to
	 * 	 @param p_server   - indicate if we have to use server patterns.
	 *   @throws IOException - if failed in exe command I/O exception occur
	 *   @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void addMatchRule(String p_port, String p_protocol,  boolean p_server) throws IOException, RuntimeException
	{
		addMatchRuleDir( p_port, p_protocol, true, p_server);
		addMatchRuleDir( p_port, p_protocol, false, p_server);
	}

	/**
	 * This method add match rule into the IPTable. match rule it rule that base on port & protocol
	 * * @param p_port - the port that the rule refer to
	 * 	 @param p_protocol - the protocol that the rule refer to
	 * 	 @param p_inDirect - INPUT or OUTPUT chain
	 * 	 @param p_server   - indicate if we have to use server patterns.
	 *   @throws IOException - if failed in exe command I/O exception occur
	 *   @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void addMatchRuleDir(String p_port, String p_protocol,  boolean p_inDirect, boolean p_server) throws IOException, RuntimeException
	{
		String filePattern = ( p_server ^ p_inDirect ) ? PatternFileIn : PatternFileOut;

		// TODO: leonid - insert the logger string
		String tmpPatternConst = patternByProtocol(filePattern, p_protocol);
		
//		run(new String[] {"iptablesFile","-A","INPUT", "-m", "ipcontext", 
//		         "--ipcontext_port", "89", "--ipcontext_protocol",
//		        "HTTP"  ,"--ipcontext_regexp" ,
//		        "http ddd   dddd ggggg",
//		        "--ipcontext_dir","1"});
//		
		run(iptablesFile +  " -t  " + iptableTableDefaultConst
				    + "-A  " + ((p_inDirect) ? iptableTargetDefaultInConst :
					iptableTargetDefaultOutConst)
					+ "-m ipcontext "
					+ "--ipcontext_port "     + p_port      + " "
					+ "--ipcontext_protocol " +  p_protocol + " "
					+ (( p_inDirect ) ? "--ipcontext_dir 1 " : "")
					+ (isLoggerOn() ? loggerIpContentConst : "" )
					+ "--ipcontext_regexp " + "\""+  tmpPatternConst + "\" -j DROP");
//		// TODO:   -j ACCEPT
	}

	
	
	/**
	 * This method add ip rule into the IPTable.
	 * * @param p_IP - the IP address that the rule refer to
	 * 	 @param p_inDirect - the direction (in/out) that the rule refer to
	 * 	    				 true = in direction, false = out direction
	 *   @param p_allow - indicate if the rule type is allow or block.
	 *   				  true= allow, false = block
	 *   @param p_index - indicated which index that rule will be insert into the IPTable.
	 *   			      if p_index  -1 the rule will be insert into the end of the IPTable
	 *   @throws IOException - if failed in exe command I/O exception occur
	 *   @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void addIPRule(String p_IP,String p_port, boolean p_inDirect, boolean p_allow, int p_index) throws IOException, RuntimeException
	{
		String prefixDestionation =  (p_inDirect) ? "s" : "d" ;
		String iptablesRule = iptablesFile
		+ " "
		+ "-t  " + iptableTableDefaultConst
		+ ((p_index == -1) ? "-A  "  : "-I ")
		+ ((p_inDirect) ? iptableTargetDefaultInConst :
			iptableTargetDefaultOutConst)
			+  ((p_index == -1) ? ""  : String.valueOf(p_index)+ " ")
			+   "-p tcp "
			+  ((p_IP.equals("") )   ? "" : (  " -"+prefixDestionation +" "    +p_IP + " "))
			+  ((p_port.equals("") ) ? ""  : (  " --"+prefixDestionation+"port "+ p_port + " "));
		  
		if ( isLoggerOn() ) {
			run( iptablesRule + loggerIpTableConst   );
		}
		
		run( iptablesRule + "-j "
				+ ((p_allow) ? "ACCEPT " : "DROP " ) );
		
	
	}


	/**
	 * This method clean all the rules from the IPTable
	 * @param N/A
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void clean() throws IOException, RuntimeException
	{
		run(iptablesFile
				+ " -t  "     + iptableTableDefaultConst
				+ " --flush " + " " );
	}

	/**
	 * This method insert into the IPTable a given rule.
	 * 		rules that insert by that function are rules with a difrent formate from matchRules/ IpRules
	 * @param p_rule - a rule to insert into the IPTables.
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void IPTable(String p_rule) throws IOException, RuntimeException
	{
		// TODO: maybee need to check the logger status
		run(iptablesFile  + p_rule);
	}

	/**
	 * This method show the list of the rules that exist in the IPTable.
	 * @param N/A.
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void list() throws IOException, RuntimeException
	{
		run(iptablesFile
				+ " -t  " + iptableTableDefaultConst
				+ " -L  --line-numbers " );
	}

	/**
	 * This method update the log's state to on/off by changing the rule's file
	 * 		formate to the fiting state.
	 * @param p_state - a flag that indicate to turn on/off the logger.
	 * 					true = turn logger on. false = turn logger off
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void log(boolean p_state) throws IOException, RuntimeException, Exception
	{
		save(IPTblFileNameTmp);
		change(p_state, IPTblFileNameTmp);
		clean();
		load(IPTblFileNameTmp);
		//run(shFileConst + "rm -f " + IPTblFileNameTmp);
	}


	/**
	 * This method present the logger's history
	 * @param N/A
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void readLog(String p_fileName) throws IOException, RuntimeException
	{
		
		String fileName = p_fileName;

		if (p_fileName.equals(""))
			fileName = IPTblLogFileNameConst;
		run( logParserFileConst  +" " + fileName);
	}

	/**
	 * This method clean all the logger history
	 * @param N/A
	 * @throws IOException - if failed in exe command I/O exception occur
	 * @throws RuntimeException - if failed in exe command Runtime exception occur
	 */
	public void cleanLog() throws IOException, RuntimeException
	{
		// TODO: call ivgenia's clean logger
		//run("call ivgenia's clean logger function");
	}


	/**
	 * This method update the rule's file.
	 * 		on state - add to all the rules a string that indicate that it report to the logger
	 * 		off state - remove from all the rules a string that indicate that it report to the logger
	 * @param p_state - a flag that indicate to turn on/off the logger.
	 * 					true = turn logger on. false = turn logger off
	 * @throws IOException
	 * @throws N/A
	 */
	// TODO: change this method to private
	public void change(boolean p_state, String p_fileName) throws FileNotFoundException, IOException
	{
		FileReader rulesFileReader;
		String tmpFileContent ="";

		// Change the logger state
		if(p_state) // logger on
		{
			// TODO: To check if fiel exist it do nothing
			File tmpLoggerFile = new File(LoggerStateFileConst);
			tmpLoggerFile.createNewFile();

			// TODO: maybee check if success if(!success)
		}
		else // logger off
		{
			File tmpLoggerFile = new File(LoggerStateFileConst);
			tmpLoggerFile.delete();
			// TODO: maybee check if success if(!success) - or exception
		}
		
		//p_fileName = (DebugModeConst)? "C:\\Documents and Settings\\Administrator.CMP200\\Desktop\\Maya\\WORK_SVN\\trunk\\binary\\.tmp.ipcontext.rule":IPTblFileNameTmp;
		rulesFileReader = new FileReader(p_fileName);
		File tmpFile = new File(p_fileName);

		// Check if file exist
		if (!tmpFile.exists())
			throw (new FileNotFoundException());
		tmpFile = null;

		LineNumberReader linesfileReader = new LineNumberReader(rulesFileReader);


		for (String line = null; (line = linesfileReader.readLine()) != null;)
		{
			String tmpString = line.toString();
			// remove the logger flag from  the line
				// case of line with type X and logger off in that line (turn logger on)
			if (tmpString.indexOf(ipContentTypeConst) != -1) 
			{
				if(p_state == false)
				{
					tmpString = tmpString.replaceAll(loggerIpContentConst, "");
				}else // Add the logger flag to the line
				{
					if  (tmpString.indexOf(loggerIpContentConst) == -1) {
							tmpString += " " + loggerIpContentConst;
					}
				}
			}else if ( (tmpString.startsWith("-A INPUT") ) 
					 ||(tmpString.startsWith("-A OUTPUT") )  )  {	// case of line with type X and logger off in that line (turn logger on)
				if(p_state == false)
				{
					if  ( tmpString.indexOf(loggerIpTableConst) != -1) 
					{
						continue; //TODO : FIX IT!!! it's ugly.
					}
				}else // Add the logger flag to the line
				{
					if  (tmpString.indexOf(loggerIpTableConst) == -1) {
						String logLine = tmpString;
//						logLine.replaceAll("\\-j\\s+\\w+",
						//logLine.replreplaceAll("DROP", loggerIpContentConst);
						int i = logLine.indexOf("-j");
						tmpString = logLine.substring(0, i-1)
						                     + " "
						                     + loggerIpTableConst + "\n" 
						                     + tmpString;
					}
				}
				
				
			}
			
			// TODO: check if the "\n" is needed / neccasery
			tmpString += "\n";
			tmpFileContent += tmpString.toString();
		}

		// TODO: check if that code switch betwen the files - as needed
		rulesFileReader.close();
		FileWriter rulesFileWriter;

		rulesFileWriter= new FileWriter(p_fileName, false); // the false parm is for not append
		rulesFileWriter.write(tmpFileContent);
		rulesFileWriter.close();
	}

	public boolean isLoggerOn()
	{
		File tmpFile = new File(LoggerStateFileConst);
		if(tmpFile.exists())
			return true;
		return false;
	}
	
	 private void run(String p_systemOrder) throws IOException, RuntimeException
	 {
	  if (DebugModePrintConst)
	  {
	   System.out.println("The Order content:" + p_systemOrder);
	  }
	  if ( ! DebugModeConst){
	   String[] cmd = {"/bin/sh", "-c", p_systemOrder};
	   Process proc = Runtime.getRuntime().exec(cmd);
	   BufferedReader input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
	   BufferedReader error = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
	   String line;
	   while ((line = input.readLine()) != null)
	   {
	    System.out.println(line);
	   }
	   while ((line = error.readLine()) != null)
	   {
	    System.out.println(line);
	   }
	   input.close();
	   int retval;
	   try {
	    retval = proc.waitFor();
	    if (retval != 0)
	     throw new RuntimeException();
	   } catch (InterruptedException e) {
	    // TODO Auto-generated catch block
//	    e.printStackTrace();
	   }
//	   int retval = proc.exitValue();
	  }
	 }
	
	
//	private void run(String p_systemOrder) throws IOException, RuntimeException
//	{
//		if (DebugModePrintConst)
//		{
//			System.out.println("The Order content:" + p_systemOrder);
//		}
//		if ( ! DebugModeConst){
//			Process proc = Runtime.getRuntime().exec(p_systemOrder);
//			BufferedReader input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
//			BufferedReader error = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
//			String line;
//			while ((line = input.readLine()) != null)
//			{
//				System.out.println(line);
//			}
//			while ((line = error.readLine()) != null)
//			{
//				System.out.println(line);
//			}
//			input.close();
//			int retval;
//			try {
//				retval = proc.waitFor();
//				if (retval != 0)
//					throw new RuntimeException();
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
////				e.printStackTrace();
//			}
////			int retval = proc.exitValue();
//		}
//	}
	
	
	
//	 private void run(String[] program) throws IOException, RuntimeException
//	 {
//	  if (DebugModePrintConst)
//	  {
//	   System.out.println("The Order content: " + program.toString());
//	  }
//	  if ( ! DebugModeConst){
//
//	   Process proc = Runtime.getRuntime().exec(program);
//	   BufferedReader input = new BufferedReader(new InputStreamReader(proc.getInputStream()));
//	   BufferedReader error = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
//	   String line;
//	   while ((line = input.readLine()) != null)
//	   {
//	    System.out.println(line);
//	   }
//	   while ((line = error.readLine()) != null)
//	   {
//	    System.out.println(line);
//	   }
//	   input.close();
//	   int retval;
//	   try {
//	    retval = proc.waitFor();
//	    if (retval != 0)
//	     throw new RuntimeException();
//	   } catch (InterruptedException e) {
//	    // TODO Auto-generated catch block
////	    e.printStackTrace();
//	   }
////	   int retval = proc.exitValue();
//	  }
//	 }

	private String patternByProtocol(String p_fileName, String p_prot) throws IOException
	{
		FileInputStream fin = new FileInputStream(p_fileName);
		BufferedReader br = new BufferedReader(new InputStreamReader(fin));
		String strLine = new String(); /*read string*/
		int index;
		while ( ( (strLine = br.readLine()) != null) &&
				(!strLine.startsWith(p_prot + ":"))) {
		}
		index = strLine.indexOf(':');
		return strLine.substring(index + 2);
	}
	
	public static ArrayList getLoggerSessionsReports(int depth)
	{
		System.out.println("getLoggerSessionsReports " + depth);
		ArrayList output = new ArrayList();
		
		for(int i=0; i < 10; i++)
		{
			String[] line = {"12:00", "in", "10:10:20:1", "10:10:20:2", "80", "2078", "http", "denied" };
			output.add(line);
		}
		
		String[] line2 = {"13:00", "in", "10:10:20:1", "10:10:20:2", "80", "2078", "http", "denied" };
		output.add(line2);
		
		return output;
	}

	public static ArrayList getLoggerSessionsReports(int historyDepth, String filterOption, String filterParameter) {
		String[] line2 = {"20:00", "in", "10:10:20:1", "10:10:20:2", "80", "2078", "http", "accepted" };
		ArrayList output = new ArrayList();
		output.add(line2);
		return output;
	}

	public static ArrayList getLoggerSessionsReports(int historyDepth, String filterOption) {
		String[] line2 = {"15:00", "out", "10:10:20:1", "10:10:20:2", "80", "2078", "http", "denied" };
		ArrayList output = new ArrayList();
		output.add(line2);
		return output;
	}

	public static int getDeniedSessionsNumber(int i)
	{
		return 0;
	}

	public static int getAcceptedSessionsNumber(int historyDepth) {
		// TODO Auto-generated method stub
		return 0;
	}
}
